/******************************************************************************
 *      Mestrado em Ciência da Computação - UFMS
 *		Redes de Computadores - Prof. Dr. Ronaldo Alves Ferreira
 *		Alunos: Geraldo Landre, Marcel Tolentino, Vinícius Bueno
 *****************************************************************************/
/* tcpxmit.c - tcpxmit */

#include "tcpfms.h"
#include "tcb.h"
#include "tcp.h"
#include "my_socket.h"

extern int tmset( int port, int portlen, void *msg, int time);
extern int tmleft(int port, void *msg);
extern int tmclear(int port, void *msg);

/*------------------------------------------------------------------------
 *  tcpxmit - manipula eventos de saida TCP enquanto nós estamos transmitindo
 *  *------------------------------------------------------------------------
 */
int tcpxmit(int tcbnum, int event)
{
    struct	tcb	*ptcb = (struct tcb*)&tcbtab[tcbnum];
    void	*tv;
    int	tosend, pending, window;

    if (event == RETRANSMIT) {
        tmclear(thread_getport(output_thread), MKEVENT(SEND, tcbnum));
        tcprexmt(tcbnum, event);
        ptcb->tcb_ostate = TCPO_REXMT;
        return OK;
    } // else SEND
    tosend = tcphowmuch(ptcb);
    if (tosend == 0) {
        if (ptcb->tcb_flags & TCBF_NEEDOUT)
            tcpsend(tcbnum, TSF_NEWDATA);	/* just an ACK */
        if (ptcb->tcb_snext == ptcb->tcb_suna)
            return OK;
        /* still unacked data; restart transmit timer	*/
        tv = MKEVENT(RETRANSMIT, tcbnum);
        if (!tmleft(thread_getport(output_thread), tv))
            tmset(thread_getport(output_thread), TCPQLEN, tv, ptcb->tcb_rexmt);
        return OK;
    } else if (ptcb->tcb_swindow == 0) {
        ptcb->tcb_ostate = TCPO_PERSIST;
        ptcb->tcb_persist = ptcb->tcb_rexmt;
        tcpsend(tcbnum, TSF_NEWDATA);
        tmset(thread_getport(output_thread), TCPQLEN, MKEVENT(PERSIST,tcbnum),
                ptcb->tcb_persist);
        return OK;
    }	/* else, we have data and window */
    ptcb->tcb_ostate = TCPO_XMIT;
    window = min(ptcb->tcb_swindow, ptcb->tcb_cwnd);
    pending = ptcb->tcb_snext - ptcb->tcb_suna;
    while (tcphowmuch(ptcb) > 0 && pending < window) {
        tcpsend(tcbnum, TSF_NEWDATA);
        pending = ptcb->tcb_snext - ptcb->tcb_suna;
    }
    tv = MKEVENT(RETRANSMIT, tcbnum);
    if (!tmleft(thread_getport(output_thread), tv))
        tmset(thread_getport(output_thread), TCPQLEN, tv, ptcb->tcb_rexmt);
    return OK;
}
